package com.cqq.common.core.protocol.http;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.SocketConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.cqq.common.core.protocol.ProtocolUtil;
import com.cqq.common.core.protocol.RequestBean;
import com.cqq.common.core.protocol.ResponseBean;
import com.cqq.common.core.protocol.exception.ProtocolException;
import com.cqq.common.core.protocol.http.HttpMethod;
import com.cqq.common.core.protocol.http.HttpParamBean;
import com.cqq.common.core.protocol.http.HttpRequestBean;
import com.cqq.common.core.protocol.http.HttpResponseBean;

@Component("httpProtocolUtil")
public class HttpProtocolUtil implements ProtocolUtil {
	 
	private static Logger log = LoggerFactory.getLogger(HttpProtocolUtil.class);
	
	private static final int DEFAULT_TIMEOUT=10000;
	
	@SuppressWarnings("unchecked")
	public ResponseBean send(RequestBean request) throws ProtocolException {
		HttpRequestBean httpReq=(HttpRequestBean)request;
		String url=httpReq.getAddress();
		//String url=PatternReplacer.replace(httpReq.getAddress(),request.getHolderMap());
		String charset=httpReq.getCharset();
		int timeout=httpReq.getTimeout();
		if(timeout<=0){
			timeout=DEFAULT_TIMEOUT;
		}
		List<HttpParamBean> params=(List<HttpParamBean>)httpReq.getData();
		//CloseableHttpClient client = HttpClients.createDefault();
		ConnectionConfig connConfig=ConnectionConfig.custom().build();
		SocketConfig socketConfig=SocketConfig.custom().setSoTimeout(timeout).build();
		PoolingHttpClientConnectionManager poolConnManager=new PoolingHttpClientConnectionManager();
		poolConnManager.setDefaultConnectionConfig(connConfig);
		poolConnManager.setDefaultSocketConfig(socketConfig);
		poolConnManager.setMaxTotal(50);
		poolConnManager.setDefaultMaxPerRoute(10);
		CloseableHttpClient client = HttpClients.custom().setConnectionManager(poolConnManager).build();		
		RequestConfig reqConf=RequestConfig.custom()
				.setConnectTimeout(timeout)
				.setSocketTimeout(timeout).build();
		CloseableHttpResponse response=null;
		HttpGet httpGet=null;
		HttpPost httpPost=null;
		HttpResponseBean result=new HttpResponseBean();
		Map<String,String> headerMap=new HashMap<String,String>();
		headerMap.put("Accept-Language", "zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3");
		try{
			List<NameValuePair> nvps = new ArrayList<NameValuePair>();
			log.info("http request url: {}",url);
			log.debug("http request params:");
			String value=null;
			for(HttpParamBean p:params){
				value=p.getValue();
				//value=PatternReplacer.replace(p.getValue(),request.getHolderMap());
				nvps.add(new BasicNameValuePair(p.getName(), value));
				log.debug("{} -> {}",p.getName(),value);
			}
			UrlEncodedFormEntity form=new UrlEncodedFormEntity(nvps,charset);
			
			if(httpReq.getMethod().equals(HttpMethod.GET)){
				url=url+"?"+EntityUtils.toString(form);
				log.info("http get url: {}",url);
				httpGet = new HttpGet(url);
				for(String key:headerMap.keySet()){
					httpGet.addHeader(key, headerMap.get(key));
				}
				httpGet.setConfig(reqConf);
				response = client.execute(httpGet);
			}
			else{
				httpPost = new HttpPost(url);
				for(String key:headerMap.keySet()){
					httpPost.addHeader(key, headerMap.get(key));
				}
				httpPost.setConfig(reqConf);
				httpPost.setEntity(form);
				response = client.execute(httpPost);
			}
			int code = response.getStatusLine().getStatusCode();
			result.setCode(code);
			log.info("http status code: {}",code);
			result.setSessionID(this.getSessionID(response));
			log.info("http session id: {}",result.getSessionID());
			if(code == HttpStatus.SC_OK){
				HttpEntity entity = response.getEntity();
				//EntityUtils.consume(entity);
				result.setSuccess(true);
				result.setHasException(false);
				result.setData(EntityUtils.toString(entity));
				log.debug("http response: {}",result.getData());
			}
			else if(code == HttpStatus.SC_MOVED_PERMANENTLY
					|| code == HttpStatus.SC_MOVED_TEMPORARILY
					|| code == HttpStatus.SC_SEE_OTHER
					|| code == HttpStatus.SC_TEMPORARY_REDIRECT){
				Header header = response.getFirstHeader("location");
				httpGet.abort();
				httpGet = new HttpGet(header.getValue());
				response = client.execute(httpGet);
				HttpEntity entity = response.getEntity();
				//EntityUtils.consume(entity);
				result.setSuccess(true);
				result.setHasException(false);
				result.setData(EntityUtils.toString(entity));
				log.debug("http response: {}",result.getData());
			}
			else{
				result.setSuccess(false);
				result.setHasException(false);
			}
		}
		catch(Exception e){
			log.error(e.getMessage(),e);
			result.setSuccess(false);
			result.setHasException(false);
			result.setExceptionObject(e);
			result.setException(this.getException(e));
		}
		finally{
			if(httpGet!=null){
				httpGet.abort();
			}
			if(httpPost!=null){
				httpPost.abort();
			}
			if(response!=null){
				IOUtils.closeQuietly(response);
			}
			if(client!=null){
				IOUtils.closeQuietly(client);
			}
		}
		
		return result;
	}

	private String getSessionID(HttpResponse response){
		String sessionID=null;
		for(Header header:response.getAllHeaders()){
			if("Set-Cookie".equals(header.getName())){
				String value=header.getValue();
				value=value.split("; ")[0];
				sessionID=value.substring(value.lastIndexOf("=")+1);
				break;
			}
		}
		return sessionID;
	}
	
	private String getException(Throwable throwable) {
		StringWriter sw = new StringWriter();
		throwable.printStackTrace(new PrintWriter(sw));
		return sw.toString();
	}
	 
}
